using Newtonsoft.Json;
using Nop.Core;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Services.Authentication;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Stores;
using Spotshub.Api.Filter;
using Spotshub.Api.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Http;
using System.Web.Script.Serialization;

namespace Spotshub.Api.Controllers.V1
{
    [ExceptionHandlerAttribute]
    public class CheckoutController : ApiController
    {
        public const string Shipping = "Shipping";
        public const string Billing = "Billing";
        private readonly IWorkContext _workContext;
        private readonly ICustomerService _customerService;
        private readonly ICustomerRegistrationService _customerRegistrationService;
        private readonly IAuthenticationService _authenticationService;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly ILocalizationService _localizationService;
        private readonly OrderSettings _orderSettings;
        private readonly IOrderService _orderService;
        private readonly IOrderProcessingService _orderProcessingService;
        private readonly IOrderTotalCalculationService _orderTotalCalculationService;
        private readonly IStoreService _storeService;
        private readonly HttpContextBase _httpContext;
        private readonly IPaymentService _paymentService;
        private readonly PaymentSettings _paymentSettings;
        private readonly ICheckoutAttributeService _checkoutAttributeService;
        private readonly IGenericAttributeService _genericAttributeService;
        public CheckoutController(
            IWorkContext workContext,
            ICustomerService customerService,
            ICustomerRegistrationService customerRegistrationService,
            IAuthenticationService authenticationService,
            ICustomerActivityService customerActivityService,
            ILocalizationService localizationService,
            OrderSettings orderSettings,
            IOrderService orderService,
            IOrderProcessingService orderProcessingService,
            IOrderTotalCalculationService orderTotalCalculationService,
            IStoreService storeService,
            HttpContextBase httpContext,
            PaymentSettings paymentSettings,
            ICheckoutAttributeService checkoutAttrService,
            IGenericAttributeService genericAttributeService)
        {
            this._workContext = workContext;
            this._customerService = customerService;
            this._customerRegistrationService = customerRegistrationService;
            this._authenticationService = authenticationService;
            this._customerActivityService = customerActivityService;
            this._localizationService = localizationService;
            this._orderSettings = orderSettings;
            this._orderService = orderService;
            this._orderProcessingService = orderProcessingService;
            this._orderTotalCalculationService = orderTotalCalculationService;
            this._storeService = storeService;
            this._httpContext = httpContext;
            this._paymentSettings = paymentSettings;
            this._checkoutAttributeService = checkoutAttrService;
            this._genericAttributeService = genericAttributeService;
        }

        // GET api/Checkout/GetShippingAddress
        public Nop.Core.ApiDataModel.Address GetShippingAddress()
        {
            CheckCustomerLogin();

            if (_workContext.CurrentCustomer.ShippingAddress != null)
            {
                return new Nop.Core.ApiDataModel.Address(_workContext.CurrentCustomer.ShippingAddress);
            }
            else
            {
                return null;
            }
        }


        // GET api/Checkout/GetBillingAddress
        public Nop.Core.ApiDataModel.Address GetBillingAddress()
        {
            CheckCustomerLogin();
            if (_workContext.CurrentCustomer.BillingAddress != null)
            {
                return new Nop.Core.ApiDataModel.Address(_workContext.CurrentCustomer.BillingAddress);
            }
            else
            {
                return null;
            }
        }


        // POST api/Checkout/SelectShippingAddress/{id}
        [HttpPost]
        public bool SetShippingAddress(int id)
        {
            CheckCustomerLogin();

            var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == id).FirstOrDefault();
            if (address == null)
                return false;

            //_workContext.CurrentCustomer.ShippingAddress = address;
            //_customerService.UpdateCustomer(_workContext.CurrentCustomer);

            var strupdate = "update [Customer] set ShippingAddress_Id=" + address.Id + " where Id=" + _workContext.CurrentCustomer.Id;
            var bupdate = _customerService.ExcuteSql(strupdate, _workContext.CurrentCustomer);

            if (bupdate)
            {
                return true;
            }
            return false;
        }

        // POST api/Checkout/SelectBillingAddress/{id}
        [HttpPost]
        public bool SetBillingAddress(int id)
        {
            CheckCustomerLogin();

            var address = _workContext.CurrentCustomer.Addresses.Where(a => a.Id == id).FirstOrDefault();
            if (address == null)
                return false;

            //_workContext.CurrentCustomer.BillingAddress = address;
            //_customerService.UpdateCustomer(_workContext.CurrentCustomer);
            var strupdate = "update [Customer] set BillingAddress_Id=" + address.Id + " where Id=" + _workContext.CurrentCustomer.Id;
            var bupdate = _customerService.ExcuteSql(strupdate, _workContext.CurrentCustomer);

            if (bupdate)
            {
                return true;
            }
            return false;

        }

        // POST api/Checkout/SetPaymentInfo/{id}
        [HttpPost]
        public bool SetPaymentInfo(int id)
        {
            CheckCustomerLogin();

            return true;
        }

        [HttpGet]
        public string OrderConfirmation()
        {
            var paymentmethod = _paymentSettings.DefaultPaymentMethodSystemName;

            var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(paymentmethod);
            if (paymentMethodInst == null || !paymentMethodInst.IsPaymentMethodActive(_paymentSettings))
                throw new Exception("Payment method can't be parsed: {0}");

            _workContext.CurrentCustomer.SelectedPaymentMethodSystemName = paymentmethod;
            _customerService.UpdateCustomer(_workContext.CurrentCustomer);

            return "url";
        }

        [HttpPost]
        public GiftMessageResult AddGiftMessage([FromBody]GiftMessageModel model)
        {
            if (model == null || string.IsNullOrEmpty(model.Sender) ||
                string.IsNullOrEmpty(model.Receiver) || string.IsNullOrEmpty(model.Message))
                return new GiftMessageResult { Message = "Sender, Receiver and Message cannot be empty", Success = false };
            var allAttrs = _checkoutAttributeService.GetAllCheckoutAttributes();
            var senderAttribute = allAttrs.FirstOrDefault(attr => attr.Name.Equals("sender", StringComparison.OrdinalIgnoreCase));
            var receiverAttribute = allAttrs.FirstOrDefault(attr => attr.Name.Equals("receiver", StringComparison.OrdinalIgnoreCase));
            var messageAttribute = allAttrs.FirstOrDefault(attr => attr.Name.Equals("message", StringComparison.OrdinalIgnoreCase));
            if (senderAttribute == null || receiverAttribute == null || messageAttribute == null)
                return new GiftMessageResult { Message = "Failed to Add Gift Message", Success = false };

            CheckoutAttributeParser parser = new CheckoutAttributeParser(_checkoutAttributeService);
            string attributesXml = "";
            attributesXml = parser.AddCheckoutAttribute(attributesXml, senderAttribute, model.Sender);
            attributesXml = parser.AddCheckoutAttribute(attributesXml, receiverAttribute, model.Receiver);
            attributesXml = parser.AddCheckoutAttribute(attributesXml, messageAttribute, model.Message);
            _workContext.CurrentCustomer.CheckoutAttributes = attributesXml;
            var customer = _workContext.CurrentCustomer;
            _genericAttributeService.AppSaveAttribute(customer, SystemCustomerAttributeNames.CheckoutAttributes, attributesXml);
            return new GiftMessageResult { Message = "Successfully to Add Gift Message", Success = true };
        }

        [HttpGet]
        public PlaceOrderResultModel PlaceOrder()
        {
            //prevent 2 orders being placed within an X seconds time frame
            if (!IsMinimumOrderPlacementIntervalValid(_workContext.CurrentCustomer))
                return new PlaceOrderResultModel { Success = false, Message = _localizationService.GetResource("Checkout.MinOrderPlacementInterval") };

            var storeId = GetStoreId();
            var cart = GetCart(storeId);
            var storeOrder = _storeService.GetStoreShoppingCartItems(cart);
            storeOrder.ProcessPaymentRequest.PaymentMethodSystemName = "";
            //place order
            ProcessPaymentRequest processPaymentRequest = new ProcessPaymentRequest();
            processPaymentRequest.CustomerId = _workContext.CurrentCustomer.Id;
            processPaymentRequest.PaymentMethodSystemName = _workContext.CurrentCustomer.SelectedPaymentMethodSystemName;

            storeOrder.ProcessPaymentRequest = processPaymentRequest;

            if (_workContext.CurrentCustomer.Addresses == null || _workContext.CurrentCustomer.Addresses.Count <= 0)
            {
                return new PlaceOrderResultModel() { OrderId = -1, Success = false, Message = _localizationService.GetResource("Checkout.Customer.ShoppingOrBillAdressIsNull"), ErrorCode = AddressErrorStatus.CustomerAddressIsNull };
            }

            if (_workContext.CurrentCustomer.ShippingAddress == null &&
                _workContext.CurrentCustomer.BillingAddress == null)
            {
                return new PlaceOrderResultModel() { OrderId = -1, Success = false, Message = _localizationService.GetResource("Checkout.Customer.AdressIsNull"), ErrorCode = AddressErrorStatus.BillAdressOrShippingAddress };
            }

            if (_workContext.CurrentCustomer.ShippingAddress == null)
            {
                return new PlaceOrderResultModel() { OrderId = -1, Success = false, Message = _localizationService.GetResource("Checkout.Customer.ShippingAddress"), ErrorCode = AddressErrorStatus.ShippingAddressNull };
            }
            if (_workContext.CurrentCustomer.BillingAddress == null)
            {
                return new PlaceOrderResultModel() { OrderId = -1, Success = false, Message = _localizationService.GetResource("Checkout.Customer.BillingAddress"), ErrorCode = AddressErrorStatus.BillAddressNull };
            }

            var result = _orderProcessingService.PlaceOrder(storeOrder, true);

            if (result.Success)
            {
                //foreach (var order in result.PlacedOrders)
                //{
                //    var postProcessPaymentRequest = new PostProcessPaymentRequest()
                //    {
                //        Order = order
                //    };
                //    _paymentService.PostProcessPayment(postProcessPaymentRequest);
                //}
                if (result.PlacedOrders != null && result.PlacedOrders.Count > 0)
                {

                    return new PlaceOrderResultModel() { OrderId = result.PlacedOrders[0].Id, OrderTotal = result.PlacedOrders[0].OrderTotal, Success = true };
                }
            }
            return new PlaceOrderResultModel() { OrderId = -1, Success = false, Message = result.Errors.Any() ? result.Errors.First().Trim(';') : "", ErrorCode = result.ErrorCode };
        }


        #region helper methods
        private HttpResponseMessage ObjectToJson(object obj)
        {
            string str;
            if (obj is string || obj is char)
            {
                str = obj.ToString();
            }
            else
            {
                var serializer = new JavaScriptSerializer();
                str = serializer.Serialize(obj);
            }
            var result = new HttpResponseMessage { Content = new StringContent(str, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }
        private void CheckCustomerLogin()
        {
            if (_workContext.CurrentCustomer.IsGuest())
                throw new Exception("Anonymous checkout is not allowed");
        }

        protected List<ShoppingCartItem> GetCart(int storeId)
        {
            return _workContext.CurrentCustomer.ShoppingCartItems
                .Where(sci => sci.ShoppingCartType == ShoppingCartType.ShoppingCart)
                .ToList();
        }

        protected int GetStoreId()
        {
            return Nop.Core.Domain.Stores.Store.AllStoreReservedId;
        }

        protected bool IsPaymentWorkflowRequired(IList<ShoppingCartItem> cart, bool ignoreRewardPoints = false)
        {
            bool result = true;

            //check whether order total equals zero
            decimal? shoppingCartTotalBase = _orderTotalCalculationService.GetShoppingCartTotal(cart, ignoreRewardPoints);
            if (shoppingCartTotalBase.HasValue && shoppingCartTotalBase.Value == decimal.Zero)
                result = false;
            return result;
        }

        protected bool IsMinimumOrderPlacementIntervalValid(Customer customer)
        {
            //prevent 2 orders being placed within an X seconds time frame
            if (_orderSettings.MinimumOrderPlacementInterval == 0)
                return true;

            var lastOrderPlaced = _orderService.GetOrdersByCustomerId(_workContext.CurrentCustomer.Id)
                .FirstOrDefault();
            if (lastOrderPlaced == null)
                return true;

            var interval = DateTime.UtcNow - lastOrderPlaced.CreatedOnUtc;
            return interval.TotalSeconds > _orderSettings.MinimumOrderPlacementInterval;
        }
        #endregion
    }
}
